package io.yichen.usbflutter.usb;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.NonNull;

import com.hoho.android.usbserial.util.SerialInputOutputManager;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.yichen.usbflutter.usb.driver.UsbConnector;
import io.yichen.usbflutter.usb.driver.UsbEngine;
import io.yichen.usbflutter.usb.driver.UsbItem;
import io.yichen.usbflutter.usb.driver.UsbResult;

public class UsbPlugin implements MethodChannel.MethodCallHandler, FlutterPlugin {

    private static final String TAG = "UsbPlugin";

    private static final String FIND_DEVICES = "findDevices";
    private static final String CONNECT = "connectDevice";
    private static final String DISCONNECT = "disconnectDevice";
    private static final String DISCONNECT_ALL = "disconnectDeviceAll";

    private UsbEngine usbEngine;
    private MethodChannel methodChannel;

    private Handler mainLooper;
    private BroadcastReceiver broadcastReceiver;

    private Context context;


    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
        if (usbEngine == null) {
            usbEngine = new UsbEngine();
        }
        String method = call.method;
        Log.e(TAG, "onMethodCall: " + method);
        if (FIND_DEVICES.equals(method)) {
            scanDevice(result);
            return;
        }
        if (CONNECT.equals(method)) {
            connectDevice(call, result);
            return;
        }
        if (DISCONNECT.equals(method)) {
            disconnectDevice(call, result);
            return;
        }
        if (DISCONNECT_ALL.equals(method)) {
            disconnectDeviceAll(result);
        }
    }

    private void disconnectDeviceAll(MethodChannel.Result result) {
        usbEngine.disconnectAll();
        result.success(UsbResult.suc().toMap());
    }

    private void disconnectDevice(MethodCall call, MethodChannel.Result result) {
        Integer deviceId = call.argument("deviceId");
        if (deviceId == null) {
            result.success(UsbResult.err().msg("缺少设备id"));
            return;
        }
        usbEngine.disconnect(deviceId);
        result.success(UsbResult.suc().toMap());
    }

    /**
     * 连接设备
     *
     * @param call
     * @param result
     */
    private void connectDevice(MethodCall call, MethodChannel.Result result) {
        Integer deviceId = call.argument("deviceId");
        Integer baudRate = call.argument("baudRate");
        if (deviceId == null) {
            result.success(UsbResult.err().msg("缺少设备id").toMap());
            return;
        }
        if (baudRate == null) {
            result.success(UsbResult.err().msg("缺少波特率").toMap());
            return;
        }
        UsbResult usbResult = usbEngine.connect(context, deviceId, baudRate, new SerialInputOutputManager.Listener() {
            @Override
            public void onNewData(byte[] data) {
                Map<String, Object> map = new HashMap<>(2);
                map.put("deviceId", deviceId);
                map.put("data", data);
                mainLooper.post(() -> methodChannel.invokeMethod("onDeviceData", map));
            }

            @Override
            public void onRunError(Exception e) {
                Map<String, Object> map = new HashMap<>(1);
                map.put("deviceId", deviceId);
                mainLooper.post(() -> methodChannel.invokeMethod("onDeviceDisconnect", map));
                usbEngine.disconnect(deviceId);
            }
        });
        result.success(usbResult.toMap());
    }

    /**
     * 扫码设备
     *
     * @param result
     */
    private void scanDevice(MethodChannel.Result result) {
        List<UsbItem> list = usbEngine.refreshList(context);
        List<Map<String, Object>> datas = new ArrayList<>();
        if (list == null || list.isEmpty()) {
            result.success(datas);
            return;
        }
        for (UsbItem item : list) {
            Map<String, Object> map = new HashMap<>();
            UsbDevice device = item.device;
            map.put("deviceName", device.getDeviceName());
            map.put("deviceId", device.getDeviceId());
            map.put("deviceProtocol", device.getDeviceProtocol());
            map.put("productId", device.getProductId());
            map.put("productName", device.getProductName());
//            map.put("serialNumber", device.getSerialNumber());
            map.put("vendorId", device.getVendorId());
            map.put("driver", item.driver != null ? 1 : 0);
//                map.put("version", device.getVersion());
            datas.add(map);
        }
        result.success(datas);
    }

    @Override
    public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
        context = binding.getApplicationContext();
        methodChannel = new MethodChannel(binding.getBinaryMessenger(), "yichen_usb");
        methodChannel.setMethodCallHandler(this);
        usbEngine = new UsbEngine();
        _init(binding);
    }

    @Override
    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
        binding.getApplicationContext().unregisterReceiver(broadcastReceiver);
        methodChannel.setMethodCallHandler(null);
        methodChannel = null;
        if (usbEngine != null) {
            usbEngine.destroy();
        }
        usbEngine = null;
    }

    private void _init(FlutterPluginBinding binding) {
        broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (UsbConnector.INTENT_ACTION_GRANT_USB.equals(intent.getAction())) {
                    int usbPermission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
                            ? 1 : 0;
                    Map<String, Object> map = new HashMap<>(1);
                    map.put("permission", usbPermission);
                    mainLooper.post(() -> methodChannel.invokeMethod("onUsbPermission", map));
                }
            }
        };
        binding.getApplicationContext().registerReceiver(broadcastReceiver,
                new IntentFilter(UsbConnector.INTENT_ACTION_GRANT_USB));
        mainLooper = new Handler(Looper.getMainLooper());
    }
}
